package epoch.game.core.serialize;

import static epoch.game.core.serialize.WireFormat.ARRAY;
import static epoch.game.core.serialize.WireFormat.BOOLEAN;
import static epoch.game.core.serialize.WireFormat.BYTE;
import static epoch.game.core.serialize.WireFormat.CHAR;
import static epoch.game.core.serialize.WireFormat.COLLECTION;
import static epoch.game.core.serialize.WireFormat.CONHASHMAP;
import static epoch.game.core.serialize.WireFormat.DISTRIBUTED;
import static epoch.game.core.serialize.WireFormat.DOUBLE;
import static epoch.game.core.serialize.WireFormat.ENUM;
import static epoch.game.core.serialize.WireFormat.FLOAT;
import static epoch.game.core.serialize.WireFormat.HASHMAP;
import static epoch.game.core.serialize.WireFormat.INT;
import static epoch.game.core.serialize.WireFormat.LINKHASHMAP;
import static epoch.game.core.serialize.WireFormat.LINKLIST;
import static epoch.game.core.serialize.WireFormat.LIST;
import static epoch.game.core.serialize.WireFormat.LONG;
import static epoch.game.core.serialize.WireFormat.NULL;
import static epoch.game.core.serialize.WireFormat.OBJECT;
import static epoch.game.core.serialize.WireFormat.SET;
import static epoch.game.core.serialize.WireFormat.SHORT;
import static epoch.game.core.serialize.WireFormat.STRING;
//import static magician.game.core.serialize.WireFormat.MSG;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import epoch.game.core.buffer.IBuffer;
import epoch.game.core.common.Loggers;
import epoch.game.core.exception.SysException;

public class StreamReader implements IStreamReader {

	public static int DEFUALT_BUFFER_SIZE = 512;
	// 读取流
	private IBuffer buffer;

	public StreamReader() {
		buffer = IBuffer.alloc(0);
	}

	public StreamReader(byte[] data) {
		buffer = IBuffer.alloc(data.length);
		buffer.writeBytes(data);
	}
	
	public StreamReader(IBuffer buffer) {
		this.buffer = buffer;
	}
	@Override
	public byte readByte() throws IOException {
		return buffer.readByte();
	}

	@Override
	public short readShort() throws IOException {
		return buffer.readShort();
	}

	@Override
	public char readChar() throws IOException {
		return buffer.readChar();
	}
	
	@Override
	public int readInt() throws IOException {
		return buffer.readInt();
	}

	@Override
	public long readLong() throws IOException {
		return buffer.readLong();
	}

	@Override
	public boolean readBoolean() throws IOException {
		return buffer.readByte() == 1;
	}

	@Override
	public String readString() throws IOException {
		byte type = buffer.readByte();
		if (type == WireFormat.NULL) {
			return null;
		}
		int len = buffer.readInt();
		byte[] bytes = new byte[len];
		buffer.readBytes(bytes);
		try {
			return new String(bytes, WireFormat.STRING_CHARSET);
		} catch (UnsupportedEncodingException e) {
			Loggers.MSG_LOGGER.error("readString encoding error:" + e.getMessage());
			return "";
		}
	}

	@Override
	public <T extends ISerializable> T readObject(Class<T> clasz) {
		T object = null;
		try {
			byte flag = this.readByte();
			if(flag==WireFormat.DISTRIBUTED){
				object = clasz.newInstance();
				object.readFrom(this);
			}else{
				return null;
			}		
			
		} catch (Exception ex) {
			Loggers.CORE_LOGGER.error("反序列化失败，class="+clasz,ex);
			throw new SysException("反序列化失败，class="+clasz, ex);
		}
		return object;
	}

	@Override
	public byte[] readBytes() throws IOException {
		int len = buffer.readInt();
		byte[] bytes = new byte[len];
		buffer.readBytes(bytes);
		return bytes;
	}

	@Override
	public float readFloat() throws IOException {
		return buffer.readFloat();
	}

	@Override
	public double readDouble() throws IOException {
		return buffer.readDouble();
	}
	
	
	@SuppressWarnings("unchecked")
	@Override
	public <T> T read() throws IOException{
		Object result = null;

		// 类型码
		byte wireFormat = readByte();
		// 空对象
		if (wireFormat == NULL) {
			return null;
		}
		//类型
		int wireType = (wireFormat & ~ARRAY);
		
		//是数组类型
		boolean isArray = (wireFormat & ARRAY) == ARRAY;
		
		//BYTE
		if(wireType == BYTE) {
			if(isArray) {
				result = readBytes();
			} else {
				result = readByte();
			}
		//BOOLEAN
		} else if(wireType == BOOLEAN) {
			if(isArray) {
				int arrayLen = readInt();
				boolean[] values = new boolean[arrayLen];
				for(int i = 0; i < arrayLen; i++) {
					values[i] = readBoolean();
				}
				result = values;
			} else {
				result = readBoolean();
			}
		//SHORT
		} else if(wireType == SHORT) {
			if(isArray) {
				int arrayLen = readInt();
				short[] values = new short[arrayLen];
				for(int i = 0; i < arrayLen; i++) {
					values[i] = readShort();
				}
				result = values;
			} else {
				result = readShort();
			}
		//CHAR
		} else if(wireType == CHAR) {
			if(isArray) {
				int arrayLen = readInt();
				char[] values = new char[arrayLen];
				for(int i = 0; i < arrayLen; i++) {
					values[i] = readChar();
				}
				result = values;
			} else {
				result = readChar();
			}
		//INT
		} else if(wireType == INT) {
			if(isArray) {
				int arrayLen = readInt();
				int[] values = new int[arrayLen];
				for(int i = 0; i < arrayLen; i++) {
					values[i] = readInt();
				}
				result = values;
			} else {
				result = readInt();
			}
		//LONG
		} else if(wireType == LONG) {
			if(isArray) {
				int arrayLen = readInt();
				long[] values = new long[arrayLen];
				for(int i = 0; i < arrayLen; i++) {
					values[i] = readLong();
				}
				result = values;
			} else {
				result = readLong();
			}
		//FLOAT
		} else if(wireType == FLOAT) {
			if(isArray) {
				int arrayLen = readInt();
				float[] values = new float[arrayLen];
				for(int i = 0; i < arrayLen; i++) {
					values[i] = readFloat();
				}
				result = values;
			} else {
				result = readFloat();
			}
		//DOUBLE
		} else if(wireType == DOUBLE) {
			if(isArray) {
				int arrayLen = readInt();
				double[] values = new double[arrayLen];
				for(int i = 0; i < arrayLen; i++) {
					values[i] = readDouble();
				}
				result = values;
			} else {
				result = readDouble();
			}
		//STRING
		} else if(wireType == STRING) {
			if(isArray) {
				int arrayLen = readInt();
				String[] values = new String[arrayLen];
				for(int i = 0; i < arrayLen; i++) {
					values[i] = readString();
				}
				result = values;
			} else {
				result = readString();
			}
		//ENUM
		} else if (wireType == ENUM) {
			// 实际类型
			String className = readString();
			String val = readString();
			try {
				// 创建实例
				@SuppressWarnings("rawtypes")
				Class cls = Class.forName(className);
				result = Enum.valueOf(cls, val);
			} catch (ClassNotFoundException ex) {
				throw new SysException("发现无法被反序列化的ENUM类型: wireType={}, isArray={}, className={}, val={}", wireType, isArray,className,val);
			}
		//COLLECTION LIST SET
		} else if(wireType == COLLECTION || wireType == LIST || wireType == SET || wireType == LINKLIST) {
			//长度
			int len = readInt();			
			//类型
			Collection<?> list;
			if(wireType == LIST) list = new ArrayList<>();
			else if(wireType == SET) list = new HashSet<>();
			else if (wireType == LINKLIST) list = new LinkedList<>();
			else list = new ArrayList<>();	//未知Collection的具体实现 暂时一律使用arrayList子类的实现
			
			//填充数据
			for(int i = 0; i < len; i++) {
				list.add(this.read());
			}
			result = list;						
		//MAP
		} else if(wireType == LINKHASHMAP) {
			//长度
			int len = readInt();			
			//数据
			Map<Object,Object> map = new LinkedHashMap<>();
			for(int i = 0; i < len; i++) {
				Object key = this.read();
				Object val = this.read();				
				map.put(key, val);
			}			
			result = map;
		//MAP
		} else if(wireType == HASHMAP) {
			//长度
			int len = readInt();			
			//数据
			Map<Object,Object> map = new HashMap<>();
			for(int i = 0; i < len; i++) {
				Object key = this.read();
				Object val = this.read();				
				map.put(key, val);
			}			
			result = map;
		//MAP
		} else if(wireType == CONHASHMAP) {
			//长度
			int len = readInt();			
			//数据
			Map<Object,Object> map = new ConcurrentHashMap<>();
			for(int i = 0; i < len; i++) {
				Object key = this.read();
				Object val = this.read();				
				map.put(key, val);
			}			
			result = map;
		//IDistributedSerilizable接口
		} else if(wireType == DISTRIBUTED) {
			String className = readString();
			try {
				Class<?> cls = Class.forName(className);
				if (isArray) {
					int n = readInt();				
					ISerializable[] values = (ISerializable[])Array.newInstance(cls, n);
					
			    	Constructor<?> constructor = cls.getDeclaredConstructor();
		    		boolean accessible = constructor.isAccessible();
			    	constructor.setAccessible(true);
			    	for (int i=0; i<n; ++i) {
		    			byte flag = this.readByte();
		    			if(flag==WireFormat.DISTRIBUTED) {
			    			ISerializable seriable = (ISerializable)constructor.newInstance();
			    			seriable.readFrom(this);
			    			values[i] = seriable;
			    		} else {
			    			values[i] = null;
			    		}
			    	}
			    	if(!accessible){
						constructor.setAccessible(false);
					}
					result = values;
				} else {			
			    	Constructor<?> constructor = cls.getDeclaredConstructor();
					boolean accessible = constructor.isAccessible();
			    	constructor.setAccessible(true);
					ISerializable seriable = (ISerializable)constructor.newInstance();
					if(!accessible){
						constructor.setAccessible(false);
					}
					seriable.readFrom(this);
					result = seriable;
				}
			} catch (Exception e) {		
				Loggers.CORE_LOGGER.error("发现无法被反序列化的DISTRIBUTED类型",e);
				throw new SysException("发现无法被反序列化的DISTRIBUTED类型: wireType={}, isArray={}, className={}", wireType, isArray,className);
			}
		} else if(wireType == OBJECT && isArray) {
			int n = readInt();
			String className = readString();
			try {
				Class<?> cls = Class.forName(className);
				Object[] values = (Object[])Array.newInstance(cls, n);
				for (int i=0; i<n; ++i) {
					values[i] = read();
				}
				result = values;
			} catch (Exception e) {		
				Loggers.CORE_LOGGER.error("发现无法被反序列化的DISTRIBUTED类型",e);
				throw new SysException("发现无法被反序列化的DISTRIBUTED类型: wireType={}, isArray={}, className={}", wireType, isArray,className);
			}
		//其余一律不支持
		} else {
			throw new SysException("发现无法被Distributed反序列化的类型: wireType={}, isArray={}", wireType, isArray);
		}
		
		//返回值
		return (T) result;
	}

}
